/*****************************************************************************\

Microsoft Research Asia
Copyright (c) Microsoft Corporation. All Rights Reserved.

Module Name:
  TArray.h

Notes:
  This module is the implementation of array class.

History:
  Created on 7/3/2004 by t-rxiao@microsoft.com
  Modified on 7/6/2004 by i-tabao@microsoft.com
     code clean

\*****************************************************************************/

#pragma once
#include <wtypes.h>
#include <assert.h>
namespace TMAT
{

//=============================================================================
//        Template functions
//=============================================================================

template <class T> 
inline T TMin(T a, T b) { return a > b ? b : a; }

template <class T> 
inline T TMax(T a, T b) { return a > b ? a : b; }

template <class T> 
inline T TAbs(T a) { return a < 0 ? -a : a; }

// If Value>=Low && Value<High return true
// else return false
template <class T> 
inline bool TIsInRange(T Value, T Low, T High) { return (Value >= Low && Value < High) ? true : false; }

template <class T> 
inline void TSwap(T &a, T &b)
{
    T tmp;
    tmp = a;
    a = b; 
    b = tmp;
}

template <class T> 
inline T* TArrayInit(T* pArray, size_t nCount)
{   
    if (pArray)
    {
        ZeroMemory(pArray, sizeof(T) * nCount);
    }
    return pArray;
}

template <class T> 
inline T* TArrayDupReverse(const T* pArraySrc, size_t nCount)
{
    T* retVal = NULL;
    if (pArraySrc)
    {
        retVal = new T[nCount];
        if (retVal)
        {
            for (size_t i = 0; i < nCount; i++)
            {
                retVal[nCount - 1 - i] = pArraySrc[i];
            }
        }
    }
    return retVal;
}

template <class T> 
inline T* TArrayReverse(T* pArray, size_t nCount)
{
    if (pArray)
    {
        T* pBuf = TArrayDupReverse(pArray, nCount);
        if (pBuf)
        {
            TArrayCpy(pArray, pBuf, nCount);
            delete[] pBuf;
        }
    }
    return pArray;
}

template <class T> inline T* TArrayDup(const T* pArraySrc, size_t nCount)
{
    T* retVal = NULL;
    if (pArraySrc)
    {
        retVal = new T[nCount];
        if (retVal)
        {
            TArrayCpy(retVal, pArraySrc, nCount);
        }
    }
    return retVal;
}

template <class T> 
inline T* TArraySet(T* pArray, T value, size_t nCount)
{   
    if (pArray)
    {
        for (unsigned i = 0; i < nCount; i++)
        {
            pArray[i] = value;
        }
    }
    return pArray;
}

//return the pArrayDest;
template <class T> 
inline T* TArrayCpy(T* pArrayDest, const T* pArraySrc, size_t nCount)
{
    T* pRet = pArrayDest;
    if (pArrayDest && pArraySrc)
    {
        CopyMemory(pArrayDest, pArraySrc, sizeof(T) * nCount);
        pRet = pArrayDest;
    }
    return pRet;
}

//return the value of pArrayDest
template <class T> 
inline T* TArrayCpyReverse(T* pArrayDest, const T* pArraySrc, size_t nCount)
{
    if (pArrayDest && pArraySrc)
    {
        for (size_t i = 0; i < nCount; i++)
        {
            pArrayDest[nCount - 1 - i] = pArraySrc[i];
        }
    }
    return pArrayDest;
}

//The TArrayMove function copies count bytes of characters from 
//src to dest. If some regions of the source area and the destination 
//overlap, memmove ensures that the original source bytes in the 
//overlapping region are copied before being overwritten.
template <class T> 
inline T* TArrayMove(T* pArrayDest, const T* pArraySrc, size_t nCount)
{   
    T* pRet = pArrayDest;
    if (pArrayDest && pArraySrc)
    {
        pRet = MoveMemory(pArrayDest, pArraySrc, sizeof(T) * nCount);
    }
    return pRet;
}

//Compare characters in two Array
template <class T> 
inline int TArrayCmp(const T* pArrayDest, const T* pArraySrc, size_t nCount)
{   
    T* pRet = pArrayDest;
    if (pArrayDest && pArraySrc)
    {
        pRet = memcmp(pArrayDest, pArraySrc, sizeof(T) * nCount);
    }
    return pRet;
}

//return the max value of pArray
template <class T> 
inline T TArrayMax(const T* pArray, size_t nCount)
{
    assert(nCount > 0);
    assert(pArray);
    T maxValue = pArray[0];
    for (size_t i = 1; i < nCount; i++)
    {
        if (pArray[i] > maxValue)
        {
            maxValue = pArray[i];
        }
    }
    return maxValue;
}

//return the min value of pArray
template <class T> inline T TArrayMin(const T* pArray, size_t nCount)
{
    assert(nCount > 0);
    assert(pArray);
    T minValue = pArray[0];
    for (size_t i = 1; i < nCount; i++)
    {
        if (pArray[i] < minValue)
        {
            minValue = pArray[i];
        }
    }
    return minValue;
}

//return the max value pos of pArray
template <class T> 
inline int TArrayMaxPos(const T* pArray, size_t nCount)
{
    assert(nCount > 0);
    assert(pArray);
    T maxValue = pArray[0];
    size_t iPos = 0;
    for (size_t i = 1; i < nCount; i++)
    {
        if (pArray[i] > maxValue) 
        {
            maxValue = pArray[i];
            iPos = i;
        }
    }
    return iPos;
}

//return the min value pos of pArray
template <class T> 
inline int TArrayMinPos(const T* pArray, size_t nCount)
{
    assert(nCount > 0);
    assert(pArray);
    T minValue = pArray[0];
    size_t iPos = 0;
    for (size_t i = 1; i < nCount; i++)
    {
        if (pArray[i] < minValue) 
        {
            minValue = pArray[i];
            iPos = i;
        }
    }
    return iPos;
}

//=============================================================================
//        TMatrix
//=============================================================================

template <typename T>
class T2DArray  
{
//    friend class TVector<T>;

    // @access Private Members
protected:

//************************************************************
//    Section 1: Protected Data Members
//*************************************************************

    // @cmember This is the pointer to the data buffer.
    T *m_data;

    // @cmember This is the width and the height of a Matrix.
    unsigned m_Rows, m_Cols;          // cols/rows used

//************************************************************
//    Section 2: Protected Internal Functions
//*************************************************************
    inline void initSize(const int rows, const int cols) 
    { 
        m_Rows = rows; 
        m_Cols = cols; 
    }
    
public:

//********************************************************
//    Section 3: Memory Allocate
//********************************************************

    // @cmember Reallocate the memory to ajust the size of matrix
    inline T* NewSize(const unsigned rows, const unsigned cols)
    {
        if (m_data)
        {
            Destroy();
        }
        if ((rows > 0) && (cols > 0))
        {
            m_data = new T[rows * cols];        
            if (m_data)
            {
                initSize(rows, cols);
                Zero();
            }
        }
        return m_data;
    }

    // @cmember Release the memory if needed.
    inline void Destroy()
    {
        if (m_data)
        {
            delete[] m_data;
            m_data = NULL;
        }
        initSize(0, 0);
    }

//********************************************************
//    Section 4: Constructors and Destructors
//********************************************************
    // @cmember Constructor
    inline T2DArray(): m_data(NULL)
    { 
        initSize(0, 0); 
    }

    // @cmember Destructor
    inline ~T2DArray()
    {
        Destroy();
    }
    
//*************************************************
//    Section 5: Get and set matrix properties
//*************************************************
    // @cmember Get the size of Matrix, just Rows*Cols
    inline unsigned GetSize() const { return m_Rows * m_Cols; }

    // @cmember Get the Rows of Matrix
    inline unsigned GetRows() const { return m_Rows; }

    // @cmember Get the Cols of Matrix
    inline unsigned GetCols() const { return m_Cols; }

    // @cmember Get the buffer pointer
    inline T* GetPtr() { return m_data; }

    // @cmember Get the buffer pointer
    inline const T* GetPtr() const { return m_data; }

//*************************************************
//    Section 6:  Access the TMatrix Data
//*************************************************
    // @cmember Set a 0 to all element of matrix
    inline void Zero(void) 
    { 
        if (m_data)
        {
            ZeroMemory(m_data, m_Cols * m_Rows * sizeof(T));
        }
    }

    /************************************************
        Section 6.3: Row and column operation
    ************************************************/

    // @cmember Move the pointer to the next row of TMatrix
    inline void GotoNextRow(T* &ptr) const { ptr += m_Cols; }

    // @cmember return a row vector ptr, base 0
    inline T* GetRowPtr(unsigned row)
    { 
        T* pRet = 0;
        if (m_data && (row < m_Rows))
        {
            pRet = m_data + row * m_Cols;
        }
        return pRet;
    }

    // @cmember return a row vector ptr, base 0
    inline const T* GetRowPtr(unsigned row) const
    { 
        const T* pRet = 0;
        if (m_data && (row < m_Rows))
        {
            pRet = m_data + row * m_Cols;
        }
        return pRet;
    }

    // @cmember Operator (), like [].
    inline const T& operator()(unsigned uRow, unsigned uCol) const
    {    
        assert(m_data && (uRow < m_Rows) && (uCol < m_Cols));
        return m_data[uRow * m_Cols + uCol];
    }

    // @cmember Operator (), like [].
    inline T& operator()(unsigned uRow, unsigned uCol)
    {    
        assert(m_data && (uRow < m_Rows) && (uCol < m_Cols));
        return m_data[uRow * m_Cols + uCol];
    }

    // @mfunc Operator of equal
    inline bool operator==(const T2DArray<T> &ref)
    {
        bool fRet;
        if ((m_Rows != ref.m_Rows) || (m_Cols != ref.m_Cols))
        {
            return fRet;
        }
        else
        {
            fRet = (TArrayCmp(m_data, ref.GetPtr(), GetSize()) == 0);
        }

        return fRet;
    }

    // @mfunc Operator of equal
    inline bool operator!=(const T2DArray<T> &ref)
    {
        return !(operator==(ref));    
    }

    inline HRESULT Dup(T2DArray &ref)
    {
        HRESULT hr;
        int nSize = ref.GetSize();
        if (nSize <= 0) 
        {
            hr = E_INVALIDARG;
        }
        else
        {
            hr = S_OK;
            if (nSize != GetSize())
            {
                if (!NewSize(ref.GetRows(), ref.GetCols()))
                {
                    hr = E_OUTOFMEMORY;
                }
            }
            if (SUCCEEDED(hr))
            {
                TArrayCpy(GetPtr(), ref.GetPtr(), nSize);
            }
        }
        return hr;
    }

    inline HRESULT Read(BYTE *pBuf, unsigned uLen)
    {
        HRESULT hr;
        const unsigned uMAT_HEADER_LEN = 12;        // 12 is the size of mat header
        if ((uLen < uMAT_HEADER_LEN) || !pBuf || (pBuf[0] != sizeof(T)) || (pBuf[1] != 'M')
            ||  (pBuf[2] != 'A') || (pBuf[3] != 'T'))
        {
            hr = E_INVALIDARG;
        }
        else
        {
            pBuf += 4;
            unsigned uRow = *(unsigned*)pBuf;
            pBuf += 4;
            unsigned uCol = *(unsigned*)pBuf;
            if (!NewSize(uRow, uCol))
            {
                hr = E_OUTOFMEMORY;
            }
            else
            {
                if (GetSize() > uLen - uMAT_HEADER_LEN)
                {
                    hr = E_INVALIDARG;
                }
                else
                {
                    CopyMemory(m_data, pBuf + 4, GetSize() * sizeof(T));
                    hr = S_OK;
                }
            }
        }
        return hr;
    }

    inline HRESULT Read(LPCTSTR szFname)
    {
        HRESULT hr;
        if (!szFname)
        {
            hr = E_INVALIDARG;
        }
    
        else 
        {
            FILE * fp = _tfopen(szFname, _T("rb"));
            if (!fp)
            {       
                hr = E_ACCESSDENIED;
            }
            else
            {
                if (fseek(fp, 0, SEEK_END) == 0)
                {
                    unsigned int uLen = ftell(fp);
                    if (fseek(fp, 0, SEEK_SET) == 0)
                    {
                        BYTE* pBuf = new BYTE[uLen];
                        if (!pBuf)
                        {
                            hr = E_OUTOFMEMORY;
                        }
                        else 
                        {
                            if (fread(pBuf, sizeof(BYTE), uLen, fp) < (size_t) uLen)
                            {
                                hr = E_ACCESSDENIED;
                            }
                            else
                            {
                                hr = Read(pBuf, uLen);
                            }
                            delete[] pBuf;
                        }
                    }
                    else
                    {
                        hr = E_ACCESSDENIED;
                    }
                }
                else
                {
                    hr = E_ACCESSDENIED;
                }
                fclose(fp);
            }
        }
        return hr;
    }

};  //end of T2DArray

template <typename T>
class T1DArray  
{
    // @access Private Members
protected:
//***************************************************
//    Section 1:    Protected Data Members
//***************************************************

    // @cmember This is the pointer to the data buffer.
    T *m_data;

    // @cmember This is the dim of a Vector.
    unsigned m_Dim;

//***************************************************
//    Section 2:    Protected Internal Functions
//***************************************************
    // Init function, only for constructor
    inline void initSize(unsigned nDim) { m_Dim = nDim; }
    // @access Public Members

public:

//********************************************************
//        Section 3: Memory Allocation and DeAlloc
//********************************************************/

    // @cmember Reallocate the memory to ajust the size of Vector
    inline T* NewSize(const unsigned nDim)
    {
        if (m_data)
        {
            Destroy();
        }
        if (nDim > 0)
        {
            m_data = new T[nDim];
            if (m_data)
            {
                initSize(nDim);
                Zero();
            }
        }
        return m_data;
    }

    // @cmember Release the memory if needed.
    inline void Destroy()
    {
        if (m_data)
        {
            delete[] m_data;
            m_data = NULL;
        }
        initSize(0);
    }

//********************************************************
//        Section 4: Constructors and Destructors
//********************************************************/
    // @cmember Constructor
    inline T1DArray() : m_data(NULL) { initSize(0); }

    // @cmember Destructor
    inline ~T1DArray()
    {
        Destroy();
    }    
    
//*************************************************
//        Section 5: Get and set Vector properties
//*************************************************
    // @cmember Get the size of vector
    inline unsigned GetDim() const { return m_Dim; }

	// @cmember Set the size of vector
	inline void SetDim(unsigned dim)
	{
		if ((dim >= 0) && (dim < m_Dim))
			m_Dim = dim;
	}
    
    // @cmember Get the buffer pointer
    inline const T* GetPtr() const { return m_data; }
    
    inline T* GetPtr() { return m_data; }

//***************************************************
//    Section 6: Data Operating
//***************************************************

    // @cmember Set 0 to all element of Vector
    inline void Zero() 
    { 
        if (m_data) 
        {
            ZeroMemory(m_data, m_Dim * sizeof(T)); 
        }
    }


//***************************************************
//    Section 7:  OverRide Operation 
//***************************************************
    // @cmember Operator [], used to access the element of Vector
    //        This is const version, It is necessary.
    inline const T& operator[](unsigned nDim) const 
    {
        assert(m_data && (nDim < m_Dim));
        return m_data[nDim];    
    }
    
    // @cmember Operator [], used to access the element of Vector
    inline T& operator[](unsigned nDim)
    {
        assert(m_data && (nDim < m_Dim));
        return m_data[nDim];    
    }

    // @mfunc Operator of equal
    template <typename T>
    inline bool operator==(const T1DArray<T> &ref) const
    {
        bool fRet;
        if (m_Dim != ref.m_Dim)
        {
            fRet = false;
        }
        else
        {
            fRet = ((memcmp((void*)m_data, (void*)ref.m_data, m_Dim * sizeof(T))) == 0);
        }

        return fRet;
    }

    // @cmember Operator of equal
    inline bool operator>=(const T1DArray<T> &ref) const { return Compare(ref) >= 0; }

    // @cmember Operator of equal
    inline bool operator>(const T1DArray<T> &ref) const { return Compare(ref) > 0; }

    // @cmember Operator of equal
    inline bool operator<(const T1DArray<T> &ref) const { return Compare(ref) < 0; }

    // @cmember Operator of equal
    inline bool operator<=(const T1DArray<T> &ref) const { return Compare(ref) <= 0; }

    // @cmember Operator of equal
    inline bool operator==(const T1DArray<T> &ref) const;

    // @cmember Operator of equal
    inline bool operator!=(const T1DArray<T> &ref) const { return !(*this == ref); }

    // @cmember -1 for <, 0 for ==, 1 for >
    inline int Compare(const T1DArray<T> &ref) const
    {
        int nDim = TMin(m_Dim, ref.GetDim());
        int iRet;
        int i;
        for (i = 0; i < nDim; i++) 
        {
            if (m_data[i] < ref[i])
            {
                iRet = -1;
                break;
            }
            if (m_data[i] > ref[i])
            {
                iRet = 1;
                break;
            }
        }
        if (i == nDim)
        {
            iRet = m_Dim - ref.GetDim();
        }
        return iRet;
    }

    HRESULT Dup(T1DArray &ref)
    {
        HRESULT hr;
        int nDim = ref.GetDim();
        if (nDim <= 0) 
        {
            hr = E_INVALIDARG;
        }
        else
        {
            hr = S_OK;
            if ((unsigned)nDim != GetDim())
            {
                if (!NewSize(nDim))
                {
                    hr = E_OUTOFMEMORY;
                }
            }
            if (SUCCEEDED(hr))
            {
                TArrayCpy(GetPtr(), ref.GetPtr(), nDim);
            }
        }
        return hr;
    }

};  //End of class T1DArray


}  //end of namespace TMAT
